#pragma once
#pragma once
#include <helper/SAdapterBase.h>
#include <qjsbind/qjsbind.h>
using namespace qjsbind;

SNSBEGIN

class UIntArray {
public:
	UIntArray(UINT* pBuf_, int nSize_):pBuf(pBuf_),nSize(nSize_) {

	}
	int Size() const {
		return nSize;
	}
	UINT GetAt(int i) const {
		SASSERT(i < nSize);
		return pBuf[i];
	}
	void SetAt(int i, UINT data) {
		SASSERT(i < nSize);
		pBuf[i] = data;
	}
private:
	int nSize;
	UINT* pBuf;
};
class QjsLvAdapter : public SAdapterBase
{
public:
	QjsLvAdapter() {}

	void notifyDataSetChanged()
	{
		SAdapterBase::notifyDataSetChanged();
	}

	void notifyDataSetInvalidated()
	{
		SAdapterBase::notifyDataSetInvalidated();
	}

	void notifyItemDataChanged(int iItem)
	{
		SAdapterBase::notifyItemDataChanged(iItem);
	}

	enum {
		fid_getView = 0,
		fid_getCount,
		fid_initByTemplate,
		fid_getViewTypeCount,
		fid_getItemViewType,
	};

	void setJsThis(Value& jsThis) {
		if (jsThis.IsObject())
			m_jsThis = jsThis;
		else
		{
			m_jsThis = Value();
			m_funGetView = Value();
			m_funGetItemViewType = Value();
			m_funGetViewTypeCount = Value();
			m_funGetCount = Value();
			m_funInitByTemplate = Value();
		}
	}

	BOOL initCallback(int fid, Value & cbFun) {
		if (!cbFun.IsFunction())
		{
			SLOGI2("QjsLvAdapter") << "cbFun is not a function, fid:" << fid;
			return FALSE;
		}
		BOOL bRet = TRUE;
		switch (fid) {
		case fid_getView: m_funGetView = cbFun; break;
		case fid_getCount:m_funGetCount = cbFun; break;
		case fid_initByTemplate:m_funInitByTemplate = cbFun; break;
		case fid_getViewTypeCount:m_funGetViewTypeCount = cbFun; break;
		case fid_getItemViewType:m_funGetItemViewType = cbFun; break;
		default: SLOGI2("QjsLvAdapter") << "unknown fid:" << fid; bRet = FALSE; break;
		}
		return bRet;
	}
public:
	STDMETHOD_(void, getView)(int position, IWindow* pItem, IXmlNode* xmlTemplate) OVERRIDE {
		if(!m_funGetView.IsFunction())
			return;
		Context* ctx = m_jsThis.context();
		Value args[3];
		args[0] = NewValue(*ctx, position);
		args[1] = NewValue(*ctx, pItem);
		args[2] = NewValue(*ctx, xmlTemplate);
		ctx->Call(m_jsThis, m_funGetView, 3, args);
	}

	STDMETHOD_(int, getCount)(THIS) OVERRIDE {
		if(!m_funGetCount.IsFunction())
			return 0;
		Context* ctx = m_jsThis.context();
		Value ret = ctx->Call(m_jsThis, m_funGetCount, 0, NULL);
		return ret.ToInt32();
	}

	STDMETHOD_(void, InitByTemplate)(IXmlNode* pXmlTemplate) OVERRIDE
	{
		if(!m_funInitByTemplate.IsFunction())
			return;
		Context* ctx = m_jsThis.context();
		Value args[1];
		args[0] = NewValue(*ctx, pXmlTemplate);
		ctx->Call(m_jsThis, m_funInitByTemplate, 1, args);
	}

	STDMETHOD_(int, getViewTypeCount)() OVERRIDE
	{
		if(!m_funGetViewTypeCount.IsFunction())
			return 1;
		Context* ctx = m_jsThis.context();
		Value ret = ctx->Call(m_jsThis, m_funGetViewTypeCount, 0, NULL);
		return ret.ToInt32();
	}

	STDMETHOD_(int, getItemViewType)(int position)
	{
		if(!m_funGetItemViewType.IsFunction())
			return 0;
		Context* ctx = m_jsThis.context();
		Value args[1];
		args[0] = NewValue(*ctx, position);
		Value ret = ctx->Call(m_jsThis, m_funGetItemViewType, 1, args);
		return ret.ToInt32();
	}
private:
	Value m_jsThis;
	Value m_funGetView;
	Value m_funGetItemViewType;
	Value m_funGetViewTypeCount;
	Value m_funGetCount;
	Value m_funInitByTemplate;
};

class QjsMcAdapter : public SMcAdapterBase
{
public:
	QjsMcAdapter() {}
	void notifyDataSetChanged()
	{
		SMcAdapterBase::notifyDataSetChanged();
	}

	void notifyDataSetInvalidated()
	{
		SMcAdapterBase::notifyDataSetInvalidated();
	}

	void notifyItemDataChanged(int iItem)
	{
		SMcAdapterBase::notifyItemDataChanged(iItem);
	}

	enum {
		fid_getView = 0,
		fid_getCount,
		fid_initByTemplate,
		fid_getViewTypeCount,
		fid_getItemViewType,
		fid_onSort,
		fid_getColumnName,
		fid_IsColumnVisible,
	};

	void setJsThis(Value& jsThis) {
		if (jsThis.IsObject())
			m_jsThis = jsThis;
		else
		{
			m_jsThis = Value();
			m_funGetView = Value();
			m_funGetItemViewType = Value();
			m_funGetViewTypeCount = Value();
			m_funGetCount = Value();
			m_funInitByTemplate = Value();
			m_funOnSort = m_funGetColumnName = m_funIsColumnVisible = Value();
		}
	}
	BOOL initCallback(int fid, Value & cbFun) {
		if (!cbFun.IsFunction())
		{
			SLOGW2("QjsMcAdapter") << "cbfun is not a function: " << fid; 
			return FALSE;
		}
		BOOL bRet = TRUE;
		switch (fid) {
		case fid_getView: m_funGetView = cbFun; break;
		case fid_getCount:m_funGetCount = cbFun; break;
		case fid_initByTemplate:m_funInitByTemplate = cbFun; break;
		case fid_getViewTypeCount:m_funGetViewTypeCount = cbFun; break;
		case fid_getItemViewType:m_funGetItemViewType = cbFun; break;
		case fid_onSort:m_funOnSort = cbFun; break;
		case fid_getColumnName:m_funGetColumnName = cbFun; break;
		case fid_IsColumnVisible:m_funIsColumnVisible = cbFun; break;
		default:SLOGW2("QjsMcAdapter") << "unknown fid: " << fid; bRet = FALSE; break;
		}
		return bRet;
	}
public:
	STDMETHOD_(void, getView)(int position, IWindow* pItem, IXmlNode* xmlTemplate) OVERRIDE {
		if(!m_funGetView.IsFunction())
			return;
		Context* ctx = m_jsThis.context();
		Value args[3];
		args[0] = NewValue(*ctx, position);
		args[1] = NewValue(*ctx, pItem);
		args[2] = NewValue(*ctx, xmlTemplate);
		ctx->Call(m_jsThis, m_funGetView, 3, args);
	}

	STDMETHOD_(int, getCount)(THIS) OVERRIDE {
		if(!m_funGetCount.IsFunction())
			return 0;
		Context* ctx = m_jsThis.context();
		Value ret =ctx->Call(m_jsThis, m_funGetCount, 0, NULL);
		return ret.ToInt32();
	}

	STDMETHOD_(void, InitByTemplate)(IXmlNode* pXmlTemplate) OVERRIDE
	{
		if(!m_funInitByTemplate.IsFunction())
			return;
		Context* ctx = m_jsThis.context();
		Value args[1];
		args[0] = NewValue(*ctx, pXmlTemplate);
		ctx->Call(m_jsThis, m_funInitByTemplate, 1, args);
	}

	STDMETHOD_(int, getViewTypeCount)() OVERRIDE
	{
		if(!m_funGetViewTypeCount.IsFunction())
			return 1;
		Context* ctx = m_jsThis.context();
		Value ret = ctx->Call(m_jsThis, m_funGetViewTypeCount, 0, NULL);
		return ret.ToInt32();
	}

	STDMETHOD_(int, getItemViewType)(int position)
	{
		if(!m_funGetItemViewType.IsFunction())
			return 0;
		Context* ctx = m_jsThis.context();
		Value args[1];
		args[0] = NewValue(*ctx, position);
		Value ret =ctx->Call(m_jsThis, m_funGetItemViewType, 1, args);
		return ret.ToInt32();
	}

	STDMETHOD_(BOOL, OnSort)(int iCol, UINT* pFmts, int nCols) OVERRIDE
	{
		if(!m_funOnSort.IsFunction())
			return FALSE;
		Context* ctx = m_jsThis.context();
		Value args[2];
		args[0] = NewValue(*ctx, iCol);
		UIntArray arr(pFmts, nCols);
		args[1] = NewValue(*ctx, &arr);
		Value ret = ctx->Call(m_jsThis, m_funOnSort, 2, args);
		return ret.ToBool();
	}

	STDMETHOD_(SStringW, GetColumnName)(int iCol) SCONST
	{
		if(!m_funGetColumnName.IsFunction())
			return L"";
		Context* ctx = m_jsThis.context();
		Value args[1];
		args[0] = NewValue(*ctx, iCol);
		Value ret = ctx->Call(m_jsThis, m_funGetColumnName, 1, args);
		if (ret.IsString()) {
			return S_CA2W(ret.ToStdString().c_str());
		}
		else {
			if (ret.IsException())
				ctx->DumpError();
			else
				SLOGI2("") << "invalid ret";
			return L"";
		}
	}

	STDMETHOD_(BOOL, IsColumnVisible)(THIS_ int iCol) SCONST OVERRIDE
	{
		if(!m_funIsColumnVisible.IsFunction())
			return TRUE;
		Context* ctx = m_jsThis.context();
		Value args[1];
		args[0] = NewValue(*ctx, iCol);
		Value ret =ctx->Call(m_jsThis, m_funIsColumnVisible, 1, args);
		if (ret.IsBool())
			return ret.ToBool();
		else
			return TRUE;
	}

private:
	Value m_jsThis;
	Value m_funGetView;
	Value m_funGetItemViewType;
	Value m_funGetViewTypeCount;
	Value m_funGetCount;
	Value m_funInitByTemplate;
	Value m_funOnSort;
	Value m_funGetColumnName;
	Value m_funIsColumnVisible;
};

class QjsTvAdapter : public STreeAdapterBase<int>
{
	typedef STreeAdapterBase<int> _baseCls;
public:
	enum {
		fid_getView = 0,
		fid_getViewType,
		fid_getViewTypeCount,
		fid_initByTemplate,
		fid_isViewWidthMatchParent,
	};

	QjsTvAdapter()  {}

	void notifyBranchChanged(HSTREEITEM hBranch)
	{
		_baseCls::notifyBranchChanged(hBranch);
	}
	void notifyBranchInvalidated(HSTREEITEM hBranch, bool bInvalidParents = true, bool bInvalidChildren = true)
	{
		_baseCls::notifyBranchInvalidated(hBranch, bInvalidParents, bInvalidChildren);
	}

	void notifyBranchExpandChanged(HSTREEITEM hBranch, BOOL bExpandedOld, BOOL bExpandedNew)
	{
		_baseCls::notifyBranchExpandChanged(hBranch, bExpandedOld, bExpandedNew);
	}

	void setJsThis(Value& jsThis) {
		if (jsThis.IsObject())
			m_jsThis = jsThis;
		else
		{
			m_jsThis = Value();
			m_funGetView = Value();
			m_funGetViewType = Value();
			m_funGetViewTypeCount = Value();
			m_funIsViewWidthMatchParent = Value();
			m_funInitByTemplate = Value();
		}
	}
	BOOL initCallback(int fid, Value & cbFun) {
		if (!cbFun.IsFunction()) {
			SLOGW2("QjsTvAdapter") << "cbFun is not a function, fid: " << fid;
			return FALSE;
		}
		BOOL bRet = TRUE;
		switch (fid) {
		case fid_getView: m_funGetView = cbFun; break;
		case fid_getViewType:m_funGetViewType = cbFun; break;
		case fid_initByTemplate:m_funInitByTemplate = cbFun; break;
		case fid_getViewTypeCount:m_funGetViewTypeCount = cbFun; break;
		case fid_isViewWidthMatchParent:m_funIsViewWidthMatchParent = cbFun; break;
		default:SLOGW2("QjsTvAdapter") << "unknown fid: " << fid; bRet = TRUE; break;
		}
		return bRet;
	}

public:
	STDMETHOD_(void, getView)(THIS_ HSTREEITEM hItem, IWindow* pItem, IXmlNode* pXmlTemplate) OVERRIDE
	{
		if(!m_funGetView.IsFunction())
			return;
		Context* ctx = m_jsThis.context();
		Value args[3];
		args[0] = NewValue(*ctx, hItem);
		args[1] = NewValue(*ctx, pItem);
		args[2] = NewValue(*ctx, pXmlTemplate);
		ctx->Call(m_jsThis, m_funGetView, 3, args);
	}

	STDMETHOD_(int, getViewType)(HSTREEITEM hItem) const OVERRIDE
	{
		if(!m_funGetViewType.IsFunction())
			return 0;
		Context* ctx = m_jsThis.context();
		Value args[1];
		args[0] = NewValue(*ctx, hItem);
		Value ret = ctx->Call(m_jsThis, m_funGetViewType, 1, args);
		if (ret.IsNumber()) {
			return ret.ToInt32();
		}
		else
		{
			return 0;
		}
	}

	STDMETHOD_(int, getViewTypeCount)() const OVERRIDE
	{
		if(!m_funGetViewTypeCount.IsFunction())
			return 1;
		Context* ctx = m_jsThis.context();
		Value ret = ctx->Call(m_jsThis, m_funGetViewTypeCount, 0, NULL);
		return ret.ToInt32();
	}

	STDMETHOD_(void, InitByTemplate)(IXmlNode* pXmlTemplate) OVERRIDE
	{
		if(!m_funInitByTemplate.IsFunction())
			return;
		Context* ctx = m_jsThis.context();
		Value args[1];
		args[0] = NewValue(*ctx, pXmlTemplate);
		ctx->Call(m_jsThis, m_funInitByTemplate, 1, args);
	}

	STDMETHOD_(BOOL, isViewWidthMatchParent)() const OVERRIDE
	{
		if(!m_funIsViewWidthMatchParent.IsFunction())
			return FALSE;
		Context* ctx = m_jsThis.context();
		Value ret = ctx->Call(m_jsThis, m_funIsViewWidthMatchParent, 0, NULL);
		return ret.ToBool();
	}
private:
	Value m_jsThis;
	Value m_funGetView,
		m_funInitByTemplate,
		m_funGetViewTypeCount,
		m_funGetViewType,
		m_funIsViewWidthMatchParent;
};

SNSEND

using namespace SOUI;

void Exp_QjsAdapter(qjsbind::Module* module) {
	{
		JsClass<UIntArray> jsCls = module->ExportClass<UIntArray>("UIntArray");
		jsCls.Init();
		jsCls.AddFunc("Size", &UIntArray::Size);
		jsCls.AddFunc("GetAt", &UIntArray::GetAt);
		jsCls.AddFunc("SetAt", &UIntArray::SetAt);
	}
	{
		JsClass<QjsLvAdapter> jsCls = module->ExportClass<QjsLvAdapter>("QjsLvAdapter");
		jsCls.Init();
		jsCls.AddCtor<constructor<QjsLvAdapter>>();
		jsCls.AddFunc("setJsThis", &QjsLvAdapter::setJsThis);
		jsCls.AddFunc("initCallback", &QjsLvAdapter::initCallback);
		jsCls.AddFunc( "notifyDataSetChanged", &QjsLvAdapter::notifyDataSetChanged);
		jsCls.AddFunc( "notifyDataSetInvalidated", &QjsLvAdapter::notifyDataSetInvalidated);
		jsCls.AddFunc( "notifyItemDataChanged", &QjsLvAdapter::notifyItemDataChanged);

	}
	{
		JsClass<QjsMcAdapter> jsCls = module->ExportClass<QjsMcAdapter>("QjsMcAdapter");
		jsCls.Init();
		jsCls.AddCtor<constructor<QjsMcAdapter>>();
		jsCls.AddFunc( "notifyDataSetChanged", &QjsMcAdapter::notifyDataSetChanged);
		jsCls.AddFunc( "notifyDataSetInvalidated", &QjsMcAdapter::notifyDataSetInvalidated);
		jsCls.AddFunc( "notifyItemDataChanged", &QjsMcAdapter::notifyItemDataChanged);
		jsCls.AddFunc("setJsThis", &QjsMcAdapter::setJsThis);
		jsCls.AddFunc("initCallback", &QjsMcAdapter::initCallback);
	}
	{
		JsClass<QjsTvAdapter> jsCls = module->ExportClass<QjsTvAdapter>("QjsTvAdapter");
		jsCls.Init();
		jsCls.AddCtor<constructor<QjsTvAdapter>>();
		jsCls.AddFunc( "notifyBranchChanged", &QjsTvAdapter::notifyBranchChanged);
		jsCls.AddFunc( "notifyBranchInvalidated", &QjsTvAdapter::notifyBranchInvalidated);
		jsCls.AddFunc( "notifyBranchExpandChanged", &QjsTvAdapter::notifyBranchExpandChanged);

		jsCls.AddFunc("setJsThis", &QjsTvAdapter::setJsThis);
		jsCls.AddFunc("initCallback", &QjsTvAdapter::initCallback);
	}
}